﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace StarUtils.Extension
{
    public static class Conv
    {
        public static byte ToByte(object input)
        {
            return ToByte(input, 0);
        }

        public static byte ToByte(object input, byte defaultValue)
        {
            return ToByteOrNull(input) ?? defaultValue;
        }

        public static byte? ToByteOrNull(object input)
        {
            if (byte.TryParse(input.SafeString(), out var result))
            {
                return result;
            }

            return null;
        }

        public static char ToChar(object input)
        {
            return ToChar(input, '\0');
        }

        public static char ToChar(object input, char defaultValue)
        {
            return ToCharOrNull(input) ?? defaultValue;
        }

        public static char? ToCharOrNull(object input)
        {
            if (char.TryParse(input.SafeString(), out var result))
            {
                return result;
            }

            return null;
        }

        public static short ToShort(object input)
        {
            return ToShort(input, 0);
        }

        public static short ToShort(object input, short defaultValue)
        {
            return ToShortOrNull(input) ?? defaultValue;
        }

        public static short? ToShortOrNull(object input)
        {
            if (short.TryParse(input.SafeString(), out var result))
            {
                return result;
            }

            return null;
        }

        public static int ToInt(object input)
        {
            return ToInt(input, 0);
        }

        public static int ToInt(object input, int defaultValue)
        {
            return ToIntOrNull(input) ?? defaultValue;
        }

        public static int? ToIntOrNull(object input)
        {
            if (int.TryParse(input.SafeString(), out var result))
            {
                return result;
            }

            try
            {
                double? num = ToDoubleOrNull(input, 0);
                if (!num.HasValue)
                {
                    return null;
                }

                return Convert.ToInt32(num);
            }
            catch
            {
                return null;
            }
        }

        public static long ToLong(object input)
        {
            return ToLong(input, 0L);
        }

        public static long ToLong(object input, long defaultValue)
        {
            return ToLongOrNull(input) ?? defaultValue;
        }

        public static long? ToLongOrNull(object input)
        {
            if (long.TryParse(input.SafeString(), out var result))
            {
                return result;
            }

            try
            {
                decimal? num = ToDecimalOrNull(input, 0);
                if (!num.HasValue)
                {
                    return null;
                }

                return Convert.ToInt64(num);
            }
            catch
            {
                return null;
            }
        }

        public static float ToFloat(object input, int? digits = null)
        {
            return ToFloat(input, 0f, digits);
        }

        public static float ToFloat(object input, float defaultValue, int? digits = null)
        {
            return ToFloatOrNull(input, digits) ?? defaultValue;
        }

        public static float? ToFloatOrNull(object input, int? digits = null)
        {
            if (!float.TryParse(input.SafeString(), out var result))
            {
                return null;
            }

            if (!digits.HasValue)
            {
                return result;
            }

            return (float)Math.Round(result, digits.Value);
        }

        public static double ToDouble(object input, int? digits = null)
        {
            return ToDouble(input, 0.0, digits);
        }

        public static double ToDouble(object input, double defaultValue, int? digits = null)
        {
            return ToDoubleOrNull(input, digits) ?? defaultValue;
        }

        public static double? ToDoubleOrNull(object input, int? digits = null)
        {
            if (!double.TryParse(input.SafeString(), out var result))
            {
                return null;
            }

            return (!digits.HasValue) ? result : Math.Round(result, digits.Value);
        }

        public static decimal ToDecimal(object input, int? digits = null)
        {
            return ToDecimal(input, 0m, digits);
        }

        public static decimal ToDecimal(object input, decimal defaultValue, int? digits = null)
        {
            return ToDecimalOrNull(input, digits) ?? defaultValue;
        }

        public static decimal? ToDecimalOrNull(object input, int? digits = null)
        {
            if (!decimal.TryParse(input.SafeString(), out var result))
            {
                return null;
            }

            if (!digits.HasValue)
            {
                return result;
            }

            return Math.Round(result, digits.Value);
        }

        public static bool ToBool(object input)
        {
            return ToBool(input, defaultValue: false);
        }

        public static bool ToBool(object input, bool defaultValue)
        {
            return ToBoolOrNull(input) ?? defaultValue;
        }

        public static bool? ToBoolOrNull(object input)
        {
            bool? @bool = GetBool(input);
            if (@bool.HasValue)
            {
                return @bool.Value;
            }

            if (!bool.TryParse(input.SafeString(), out var result))
            {
                return null;
            }

            return result;
        }

        private static bool? GetBool(object input)
        {
            switch (input.SafeString().ToLower())
            {
                case "0":
                case "否":
                case "不":
                case "no":
                case "fail":
                    return false;
                case "1":
                case "是":
                case "ok":
                case "yes":
                    return true;
                default:
                    return null;
            }
        }

        public static DateTime ToDate(object input)
        {
            return ToDateOrNull(input) ?? DateTime.MinValue;
        }

        public static DateTime? ToDateOrNull(object input)
        {
            if (!DateTime.TryParse(input.SafeString(), out var result))
            {
                return null;
            }

            return result;
        }

        public static Guid ToGuid(object input)
        {
            return ToGuidOrNull(input) ?? Guid.Empty;
        }

        public static Guid? ToGuidOrNull(object input)
        {
            if (!Guid.TryParse(input.SafeString(), out var result))
            {
                return null;
            }

            return result;
        }

        public static List<Guid> ToGuidList(string input)
        {
            return ToList<Guid>(input);
        }

        public static List<T> ToList<T>(string input)
        {
            List<T> list = new List<T>();
            if (string.IsNullOrWhiteSpace(input))
            {
                return list;
            }

            string[] source = input.Split(',');
            list.AddRange(from each in source
                          where !string.IsNullOrWhiteSpace(each)
                          select To<T>(each));
            return list;
        }

        public static T ToEnum<T>(object input) where T : struct
        {
            return ToEnum(input, default(T));
        }

        public static T ToEnum<T>(object input, T defaultValue) where T : struct
        {
            return ToEnumOrNull<T>(input) ?? defaultValue;
        }

        public static T? ToEnumOrNull<T>(object input) where T : struct
        {
            if (System.Enum.TryParse<T>(input.SafeString(), ignoreCase: true, out var result))
            {
                return result;
            }

            return null;
        }

        public static T To<T>(object input)
        {
            if (input == null)
            {
                return default(T);
            }

            if (input is string && string.IsNullOrWhiteSpace(input.ToString()))
            {
                return default(T);
            }

            Type type = Common.GetType<T>();
            string text = type.Name.ToLower();
            try
            {
                if (text == "string")
                {
                    return (T)(object)input.ToString();
                }

                if (text == "guid")
                {
                    return (T)(object)new Guid(input.ToString());
                }

                if (type.IsEnum)
                {
                    return EnumHelper.Parse<T>(input);
                }

                if (input is IConvertible)
                {
                    return (T)Convert.ChangeType(input, type);
                }

                return (T)input;
            }
            catch
            {
                return default(T);
            }
        }
    }
}
